/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "libzlang_msoffice.h"

struct MsExcelWorkbookSheet
{
	uint32_t		sheet_id ;
	char			*sheet_name ;
	
	struct rb_node		workbook_sheet_tree_node ;
} ;

struct MsExcelCell
{
	char			*s ;
	size_t			s_len ;
	char			*t ;
	size_t			t_len ;
	char			*v ;
	size_t			v_len ;
} ;

struct MsExcelSharedString
{
	char			*t ;
	size_t			t_len ;
} ;

struct MsExcelStyleNumberFormat
{
	uint64_t		number_format_id ;
	char			*number_format_code ;
	
	struct hlist_node	number_format_hashnode ;
} ;

struct MsExcelStyleXf
{
	uint64_t		number_format_id ;
} ;

struct MsExcelSheet
{
	char			*sheet_filename ;
	char			*sheet_name ;
	
	char			*file_encoding ;
	
	uint64_t		col_count ;
	uint64_t		row_count ;
	struct MsExcelCell	*matrix ;
	
	struct MsExcelCell	*p_cell ;
	
	struct list_head	sheet_list_node ;
} ;

struct ZlangDirectProperty_msexcel
{
	struct ZlangRuntime		*rt ;
	
	char				*msexcel_filename ;
	unzFile				unzip_file ;
	
	char				*convert_encoding ;
	
	char				*workbook_encoding ;
	struct rb_root			workbook_sheet_tree ;
	struct MsExcelWorkbookSheet	*workbook_sheet ;
	
	char				*shared_strings_encoding ;
	uint64_t			shared_strings_count ;
	struct MsExcelSharedString	*shared_strings ;
	struct MsExcelSharedString	*shared_strings_ptr ;
	
	uint64_t			number_format_hashmap_size ;
	struct hlist_head		*number_format_hashmap ;
	struct MsExcelStyleNumberFormat	*number_format ;
	
	uint64_t			xf_array_size ;
	uint64_t			xf_array_no ;
	struct MsExcelStyleXf		*xf_array ;
	
	struct MsExcelSheet		*p_sheet ;
	
	struct list_head		sheet_list ;
} ;

static void DestroyNumberFormat( struct MsExcelStyleNumberFormat *number_format )
{
	if( number_format )
	{
		if( number_format->number_format_code )
			free( number_format->number_format_code );
		
		free( number_format );
	}
	
	return;
}

int CreateNumberFormatHashMap( struct ZlangDirectProperty_msexcel *msexcel_direct_prop );
void DestroyNumberFormatHashMap( struct ZlangDirectProperty_msexcel *msexcel_direct_prop );
int LinkNumberFormatHashNode_BY_number_format_id( struct ZlangDirectProperty_msexcel *msexcel_direct_prop , struct MsExcelStyleNumberFormat *number_format );
struct MsExcelStyleNumberFormat *QueryNumberFormatHashNode_BY_number_format_id( struct ZlangDirectProperty_msexcel *msexcel_direct_prop , struct MsExcelStyleNumberFormat *number_format );

CREATE_HLIST_ARRAY( CreateNumberFormatHashMap , struct ZlangDirectProperty_msexcel , number_format_hashmap , number_format_hashmap_size )
DESTROY_HLIST_ARRAY( DestroyNumberFormatHashMap , struct ZlangDirectProperty_msexcel , number_format_hashmap , number_format_hashmap_size , struct MsExcelStyleNumberFormat , number_format_hashnode , DestroyNumberFormat )
LINK_HLISTNODE_INT( LinkNumberFormatHashNode_BY_number_format_id , struct ZlangDirectProperty_msexcel , number_format_hashmap , number_format_hashmap_size , struct MsExcelStyleNumberFormat , number_format_hashnode , number_format_id )
QUERY_HLISTNODE_INT( QueryNumberFormatHashNode_BY_number_format_id , struct ZlangDirectProperty_msexcel , number_format_hashmap , number_format_hashmap_size , struct MsExcelStyleNumberFormat , number_format_hashnode , number_format_id )

int LinkWorkbookSheetTreeBySheetId( struct ZlangDirectProperty_msexcel *e , struct MsExcelWorkbookSheet *sheet );
struct MsExcelWorkbookSheet *QueryWorkbookSheetTreeBySheetId( struct ZlangDirectProperty_msexcel *e , struct MsExcelWorkbookSheet *sheet );
void UnlinkWorkbookSheet( struct ZlangDirectProperty_msexcel *e , struct MsExcelWorkbookSheet *sheet );
struct MsExcelWorkbookSheet *TravelWorkbookSheetTree( struct ZlangDirectProperty_msexcel *e , struct MsExcelWorkbookSheet *sheet );
void DestroyWorkbookSheetTree( struct ZlangDirectProperty_msexcel *e );

LINK_RBTREENODE_INT( LinkWorkbookSheetTreeBySheetId , struct ZlangDirectProperty_msexcel , workbook_sheet_tree , struct MsExcelWorkbookSheet , workbook_sheet_tree_node , sheet_id )
QUERY_RBTREENODE_INT( QueryWorkbookSheetTreeBySheetId , struct ZlangDirectProperty_msexcel , workbook_sheet_tree , struct MsExcelWorkbookSheet , workbook_sheet_tree_node , sheet_id )
UNLINK_RBTREENODE( UnlinkWorkbookSheet , struct ZlangDirectProperty_msexcel , workbook_sheet_tree , struct MsExcelWorkbookSheet , workbook_sheet_tree_node )
TRAVEL_RBTREENODE( TravelWorkbookSheetTree , struct ZlangDirectProperty_msexcel , workbook_sheet_tree , struct MsExcelWorkbookSheet , workbook_sheet_tree_node )
void FreeWorkbookSheet( void *pv )
{
	struct MsExcelWorkbookSheet	*sheet = (struct MsExcelWorkbookSheet *) pv ;
	
	if( sheet )
	{
		if( sheet->sheet_name )
			free( sheet->sheet_name );
		free( sheet );
	}
	
	return;
}
DESTROY_RBTREE( DestroyWorkbookSheetTree , struct ZlangDirectProperty_msexcel , workbook_sheet_tree , struct MsExcelWorkbookSheet , workbook_sheet_tree_node , FreeWorkbookSheet )

static void DestroyMsSheet( struct MsExcelSheet *sheet )
{
	uint64_t		i ;
	uint64_t		count ;
	struct MsExcelCell	*p_cell = NULL ;
	
	if( sheet )
	{
		if( sheet->sheet_filename )
		{
			free( sheet->sheet_filename );
		}
		if( sheet->sheet_name )
		{
			free( sheet->sheet_name );
		}
		
		if( sheet->file_encoding )
		{
			free( sheet->file_encoding );
		}
		
		if( sheet->matrix )
		{
			count = sheet->col_count * sheet->row_count ;
			for( i = 0 , p_cell = sheet->matrix ; i < count ; i++ , p_cell++ )
			{
				if( p_cell->s )
					free( p_cell->s );
				if( p_cell->t )
					free( p_cell->t );
				if( p_cell->v )
					free( p_cell->v );
			}
			free( sheet->matrix );
		}
		
		free( sheet );
	}
	
	return;
}

static int CellLabelToPosition( char *cell_label , uint64_t *col_no , uint64_t *row_no )
{
	char		*p = cell_label ;
	
	if( 'A' <= (*p) && (*p) <= 'Z' )
		;
	else
		return -1;
	
	(*col_no) = 0 ;
	for( ; (*p) ; p++ )
	{
		if( 'A' <= (*p) && (*p) <= 'Z' )
			(*col_no) = (*col_no) * 26 + ((*p)-'A'+1) ;
		else
			break;
	}
	
	if( '0' <= (*p) && (*p) <= '9' )
		;
	else
		return -2;
	
	(*row_no) = 0 ;
	for( ; (*p) ; p++ )
	{
		if( '0' <= (*p) && (*p) <= '9' )
			(*row_no) = (*row_no) * 10 + ((*p)-'0') ;
		else
			break;
	}
	
	return 0;
}

#define XPATH_XML_ENCODING			"/xml.encoding"
#define XPATH_WORKBOOK_SHEETS_SHEET_NAME	"/workbook/sheets/sheet.name"
#define XPATH_WORKBOOK_SHEETS_SHEET_SHEETID	"/workbook/sheets/sheet.sheetId"

static int CallbackOnWorkbookXmlProperty( int type , char *xpath , int xpath_len , int xpath_size , char *propname , int propname_len , char *propvalue , int propvalue_len , char *content , int content_len , void *user )
{
	struct ZlangDirectProperty_msexcel	*msexcel_direct_prop = (struct ZlangDirectProperty_msexcel *) user ;
	int					nret = 0 ;
	
	TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "XML - CallbackOnWorkbookXmlProperty - type[0x%X] xpath[%.*s]" , type , xpath_len,xpath )
	
	if( xpath_len == sizeof(XPATH_XML_ENCODING)-1 && STRNCMP( xpath , == , XPATH_XML_ENCODING , sizeof(XPATH_XML_ENCODING)-1 ) )
	{
		msexcel_direct_prop->workbook_encoding = strndup( propvalue , propvalue_len ) ;
		if( msexcel_direct_prop->workbook_encoding == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "malloc failed , errno[%d]" , errno )
			return -1;
		}
		TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "get file encoding[%s]" , msexcel_direct_prop->shared_strings_encoding )
	}
	else if( xpath_len == sizeof(XPATH_WORKBOOK_SHEETS_SHEET_NAME)-1 && STRNCMP( xpath , == , XPATH_WORKBOOK_SHEETS_SHEET_NAME , sizeof(XPATH_WORKBOOK_SHEETS_SHEET_NAME)-1 ) )
	{
		if( msexcel_direct_prop->workbook_sheet == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "msexcel_direct_prop->workbook_sheet is null" )
			return -1;
		}
		
		if( msexcel_direct_prop->convert_encoding == NULL || msexcel_direct_prop->workbook_encoding == NULL )
		{
			msexcel_direct_prop->workbook_sheet->sheet_name = strndup( propvalue , propvalue_len ) ;
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "workbook_sheet->sheet_name[%s]" , msexcel_direct_prop->workbook_sheet->sheet_name )
		}
		else
		{
			nret = ConvertStringEncoding( msexcel_direct_prop->workbook_encoding , msexcel_direct_prop->convert_encoding , propvalue , propvalue_len , & (msexcel_direct_prop->workbook_sheet->sheet_name) , NULL , NULL ) ;
			if( nret )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "ConvertStringEncoding failed[%d]" , nret )
				return -1;
			}
		}
	}
	else if( xpath_len == sizeof(XPATH_WORKBOOK_SHEETS_SHEET_SHEETID)-1 && STRNCMP( xpath , == , XPATH_WORKBOOK_SHEETS_SHEET_SHEETID , sizeof(XPATH_WORKBOOK_SHEETS_SHEET_SHEETID)-1 ) )
	{
		if( msexcel_direct_prop->workbook_sheet == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "msexcel_direct_prop->workbook_sheet is null" )
			return -1;
		}
		
		msexcel_direct_prop->workbook_sheet->sheet_id = atol( propvalue ) ;
		TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "workbook_sheet->sheet_id[%"PRIu32"]" , msexcel_direct_prop->workbook_sheet->sheet_id )
	}
	
	return 0;
}

#define XPATH_XML			"/xml"
#define XPATH_WORKBOOK_SHEETS_SHEET	"/workbook/sheets/sheet"

static int CallbackOnWorkbookXmlNode( int type , char *xpath , int xpath_len , int xpath_size , char *node , int node_len , char *tag , int tag_len , char *properties , int properties_len , char *content , int content_len , void *user )
{
	struct ZlangDirectProperty_msexcel	*msexcel_direct_prop = (struct ZlangDirectProperty_msexcel *) user ;
	int					nret = 0 ;
	
	TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "XML - CallbackOnWorkbookXmlNode - type[0x%X] xpath[%.*s]" , type , xpath_len,xpath )
	
	if( type == FASTERXML_NODE_BRANCH && xpath_len == sizeof(XPATH_XML)-1 && STRNCMP( xpath , == , XPATH_XML , sizeof(XPATH_XML)-1 ) )
	{
		nret = TravelXmlPropertiesBuffer( properties , properties_len , type , xpath , xpath_len , xpath_size , content , content_len , CallbackOnWorkbookXmlProperty , user ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "TravelXmlPropertiesBuffer failed[%d]" , nret )
			return nret;
		}
	}
	else if( type == FASTERXML_NODE_BRANCH && xpath_len == sizeof(XPATH_WORKBOOK_SHEETS_SHEET)-1 && STRNCMP( xpath , == , XPATH_WORKBOOK_SHEETS_SHEET , sizeof(XPATH_WORKBOOK_SHEETS_SHEET)-1 ) )
	{
		msexcel_direct_prop->workbook_sheet = (struct MsExcelWorkbookSheet *)malloc( sizeof(struct MsExcelWorkbookSheet) ) ;
		if( msexcel_direct_prop->workbook_sheet == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "malloc failed , errno[%d]" , nret )
			return nret;
		}
		memset( msexcel_direct_prop->workbook_sheet , 0x00 , sizeof(struct MsExcelWorkbookSheet) );
		
		nret = TravelXmlPropertiesBuffer( properties , properties_len , type , xpath , xpath_len , xpath_size , content , content_len , CallbackOnWorkbookXmlProperty , user ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "TravelXmlPropertiesBuffer failed[%d]" , nret )
			return nret;
		}
		
		nret = LinkWorkbookSheetTreeBySheetId( msexcel_direct_prop , msexcel_direct_prop->workbook_sheet ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "LinkWorkbookSheetTreeBySheetId failed[%d]" , nret )
			return nret;
		}
		
		msexcel_direct_prop->workbook_sheet = NULL ;
	}
	
	return 0;
}

static int ParseWorkbookFileContent( struct ZlangDirectProperty_msexcel *msexcel_direct_prop , char *file_content , uint64_t file_content_len )
{
	char		xpath[ 1024 ] ;
	int		nret = 0 ;
	
	memset( xpath , 0x00 , sizeof(xpath) );
	nret = TravelXmlBuffer( file_content , xpath , sizeof(xpath) , CallbackOnWorkbookXmlNode , msexcel_direct_prop ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "TravelXmlBuffer failed[%d]" , nret )
		return nret;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "TravelXmlBuffer ok" )
		return 0;
	}
}

#define XPATH_XML_ENCODING	"/xml.encoding"
#define XPATH_SST_COUNT		"/sst.count"

static int CallbackOnSharedStringsXmlProperty( int type , char *xpath , int xpath_len , int xpath_size , char *propname , int propname_len , char *propvalue , int propvalue_len , char *content , int content_len , void *user )
{
	struct ZlangDirectProperty_msexcel	*msexcel_direct_prop = (struct ZlangDirectProperty_msexcel *) user ;
	int					nret = 0 ;
	
	TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "XML - CallbackOnSharedStringsXmlProperty - type[0x%X] xpath[%.*s]" , type , xpath_len,xpath )
	
	if( xpath_len == sizeof(XPATH_XML_ENCODING)-1 && STRNCMP( xpath , == , XPATH_XML_ENCODING , sizeof(XPATH_XML_ENCODING)-1 ) )
	{
		msexcel_direct_prop->shared_strings_encoding = strndup( propvalue , propvalue_len ) ;
		if( msexcel_direct_prop->shared_strings_encoding == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "malloc failed , errno[%d]" , errno )
			return -1;
		}
		TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "get file encoding[%s]" , msexcel_direct_prop->shared_strings_encoding )
	}
	else if( xpath_len == sizeof(XPATH_SST_COUNT)-1 && STRNCMP( xpath , == , XPATH_SST_COUNT , sizeof(XPATH_SST_COUNT)-1 ) )
	{
		msexcel_direct_prop->shared_strings_count = atol(propvalue) ;
		TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "shared_strings_count[%"PRIu64"]" , msexcel_direct_prop->shared_strings_count )
		
		msexcel_direct_prop->shared_strings = (struct MsExcelSharedString *)malloc( sizeof(struct MsExcelSharedString) * msexcel_direct_prop->shared_strings_count ) ;
		if( msexcel_direct_prop->shared_strings == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "malloc failed , errno[%d]" , nret )
			return -1;
		}
		memset( msexcel_direct_prop->shared_strings , 0x00 , sizeof(struct MsExcelSharedString) * msexcel_direct_prop->shared_strings_count );
	}
	
	return 0;
}

#define XPATH_XML		"/xml"
#define XPATH_SST		"/sst"
#define XPATH_SST_SI		"/sst/si"
#define XPATH_SST_SI_T		"/sst/si/t"
#define XPATH_SST_SI_R_T	"/sst/si/r/t"

static int CallbackOnSharedStringsXmlNode( int type , char *xpath , int xpath_len , int xpath_size , char *node , int node_len , char *tag , int tag_len , char *properties , int properties_len , char *content , int content_len , void *user )
{
	struct ZlangDirectProperty_msexcel	*msexcel_direct_prop = (struct ZlangDirectProperty_msexcel *) user ;
	int					nret = 0 ;
	
	TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "XML - CallbackOnSharedStringsXmlNode - type[0x%X] xpath[%.*s]" , type , xpath_len,xpath )
	
	if( type == FASTERXML_NODE_BRANCH && xpath_len == sizeof(XPATH_XML)-1 && STRNCMP( xpath , == , XPATH_XML , sizeof(XPATH_XML)-1 ) )
	{
		nret = TravelXmlPropertiesBuffer( properties , properties_len , type , xpath , xpath_len , xpath_size , content , content_len , CallbackOnSharedStringsXmlProperty , user ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "TravelXmlPropertiesBuffer failed[%d]" , nret )
			return nret;
		}
	}
	else if( type == (FASTERXML_NODE_ENTER|FASTERXML_NODE_BRANCH) && xpath_len == sizeof(XPATH_SST)-1 && STRNCMP( xpath , == , XPATH_SST , sizeof(XPATH_SST)-1 ) )
	{
		nret = TravelXmlPropertiesBuffer( properties , properties_len , type , xpath , xpath_len , xpath_size , content , content_len , CallbackOnSharedStringsXmlProperty , user ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "TravelXmlPropertiesBuffer failed[%d]" , nret )
			return nret;
		}
	}
	else if( type == (FASTERXML_NODE_ENTER|FASTERXML_NODE_BRANCH) && xpath_len == sizeof(XPATH_SST_SI)-1 && STRNCMP( xpath , == , XPATH_SST_SI , sizeof(XPATH_SST_SI)-1 ) )
	{
		if( msexcel_direct_prop->shared_strings_ptr == NULL )
			msexcel_direct_prop->shared_strings_ptr = msexcel_direct_prop->shared_strings ;
		else
			msexcel_direct_prop->shared_strings_ptr++;
	}
	else if( type == (FASTERXML_NODE_LEAVE|FASTERXML_NODE_BRANCH) && xpath_len == sizeof(XPATH_SST_SI)-1 && STRNCMP( xpath , == , XPATH_SST_SI , sizeof(XPATH_SST_SI)-1 ) )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "add sharedstring[%ld][%.*s]" , msexcel_direct_prop->shared_strings_ptr-msexcel_direct_prop->shared_strings , (int)(msexcel_direct_prop->shared_strings_ptr->t_len),msexcel_direct_prop->shared_strings_ptr->t )
	}
	else if( type == FASTERXML_NODE_LEAF &&
		(
			( xpath_len == sizeof(XPATH_SST_SI_T)-1 && STRNCMP( xpath , == , XPATH_SST_SI_T , sizeof(XPATH_SST_SI_T)-1 ) )
			||
			( xpath_len == sizeof(XPATH_SST_SI_R_T)-1 && STRNCMP( xpath , == , XPATH_SST_SI_R_T , sizeof(XPATH_SST_SI_R_T)-1 ) )
		)
	)
	{
		char		*t = NULL ;
		size_t		t_len ;
		
		if( msexcel_direct_prop->convert_encoding == NULL || msexcel_direct_prop->shared_strings_encoding == NULL )
		{
			t = (char*)strndup( content , content_len ) ;
			if( t == NULL )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "strndup failed , errno[%d]" , errno )
				return -1;
			}
			t_len = content_len ;
		}
		else
		{
			nret = ConvertStringEncoding( msexcel_direct_prop->shared_strings_encoding , msexcel_direct_prop->convert_encoding , content , (size_t)(content_len) , & t , NULL , & t_len ) ;
			if( nret )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "ConvertStringEncoding failed[%d] , in[%p] in_len[%zu]" , nret , t , t_len )
				return -1;
			}
		}
		
		if( msexcel_direct_prop->shared_strings_ptr->t == NULL )
		{
			msexcel_direct_prop->shared_strings_ptr->t = t ;
			msexcel_direct_prop->shared_strings_ptr->t_len = t_len ;
		}
		else
		{
			char	*new_t = NULL ;
			size_t	new_t_len ;
			size_t	new_t_buf_size ;
			
			new_t_len = msexcel_direct_prop->shared_strings_ptr->t_len + t_len ;
			new_t_buf_size = new_t_len + 1 ;
			new_t = realloc( msexcel_direct_prop->shared_strings_ptr->t , new_t_buf_size ) ;
			if( new_t == NULL )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "realloc failed , errno[%d]" , errno )
				return -1;
			}
			memcpy( new_t+msexcel_direct_prop->shared_strings_ptr->t_len , t , t_len );
			msexcel_direct_prop->shared_strings_ptr->t = new_t ;
			msexcel_direct_prop->shared_strings_ptr->t_len = new_t_len ;
			msexcel_direct_prop->shared_strings_ptr->t[msexcel_direct_prop->shared_strings_ptr->t_len] = '\0' ;
			
			free( t );
		}
	}
	
	return 0;
}

static int ParseSharedStringsFileContent( struct ZlangDirectProperty_msexcel *msexcel_direct_prop , char *file_content , uint64_t file_content_len )
{
	char		xpath[ 1024 ] ;
	int		nret = 0 ;
	
	memset( xpath , 0x00 , sizeof(xpath) );
	nret = TravelXmlBuffer( file_content , xpath , sizeof(xpath) , CallbackOnSharedStringsXmlNode , msexcel_direct_prop ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "TravelXmlBuffer failed[%d]" , nret )
		return nret;
	}
	else
	{
		if( msexcel_direct_prop->shared_strings_ptr - msexcel_direct_prop->shared_strings != msexcel_direct_prop->shared_strings_count )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "msexcel_direct_prop->shared_strings_ptr-msexcel_direct_prop->shared_strings[%ld] != msexcel_direct_prop->shared_strings_ptr_count[%"PRIu64"]" , msexcel_direct_prop->shared_strings_ptr-msexcel_direct_prop->shared_strings , msexcel_direct_prop->shared_strings_count )
			return nret;
		}
		
		TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "TravelXmlBuffer ok" )
		return 0;
	}
}

#define XPATH_STYLESSHEET_NUMFMTS_COUNT			"/styleSheet/numFmts.count"
#define XPATH_STYLESSHEET_NUMFMTS_NUMFMT_NUMFMTID	"/styleSheet/numFmts/numFmt.numFmtId"
#define XPATH_STYLESSHEET_NUMFMTS_NUMFMT_FORMATCODE	"/styleSheet/numFmts/numFmt.formatCode"
#define XPATH_STYLESSHEET_CELLXFS_COUNT			"/styleSheet/cellXfs.count"
#define XPATH_STYLESSHEET_CELLXFS_XF_NUMFMTID		"/styleSheet/cellXfs/xf.numFmtId"

#define LINK_NUMBERFORMAT_HASHNODE(_number_format_id_,_number_format_code_) \
	{ \
		struct MsExcelStyleNumberFormat		*number_format = NULL ; \
		\
		number_format = (struct MsExcelStyleNumberFormat *)malloc( sizeof(struct MsExcelStyleNumberFormat) ) ; \
		if( number_format == NULL ) \
		{ \
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "malloc failed , errno[%d]" , errno ) \
			return nret; \
		} \
		memset( number_format , 0x00 , sizeof(struct MsExcelStyleNumberFormat) ); \
		number_format->number_format_id = (_number_format_id_) ; \
		number_format->number_format_code = strdup( _number_format_code_ ) ; \
		if( number_format->number_format_code == NULL ) \
		{ \
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "strdup failed , errno[%d]" , errno ) \
			DestroyNumberFormat( number_format ); \
			return nret; \
		} \
		\
		nret = LinkNumberFormatHashNode_BY_number_format_id( msexcel_direct_prop , number_format ) ; \
		if( nret ) \
		{ \
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "malloc failed , errno[%d]" , errno ) \
			DestroyNumberFormat( number_format ); \
			return nret; \
		} \
	} \

static int CallbackOnStylesXmlProperty( int type , char *xpath , int xpath_len , int xpath_size , char *propname , int propname_len , char *propvalue , int propvalue_len , char *content , int content_len , void *user )
{
	struct ZlangDirectProperty_msexcel	*msexcel_direct_prop = (struct ZlangDirectProperty_msexcel *) user ;
	int					nret = 0 ;
	
	TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "XML - CallbackOnStylesXmlProperty - type[0x%X] xpath[%.*s]" , type , xpath_len,xpath )
	
	if( xpath_len == sizeof(XPATH_STYLESSHEET_NUMFMTS_COUNT)-1 && STRNCMP( xpath , == , XPATH_STYLESSHEET_NUMFMTS_COUNT , sizeof(XPATH_STYLESSHEET_NUMFMTS_COUNT)-1 ) )
	{
		msexcel_direct_prop->number_format_hashmap_size = (atol(propvalue)+164) * 2 ;
		TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "number_format_hashmap_size[%"PRIu64"]" , msexcel_direct_prop->number_format_hashmap_size )
		nret = CreateNumberFormatHashMap( msexcel_direct_prop ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "CreateNumberFormatHashMap failed , errno[%d]" , nret )
			return -1;
		}
		
		LINK_NUMBERFORMAT_HASHNODE( 9 , "0.%" )
		LINK_NUMBERFORMAT_HASHNODE( 10 , "0.00%" )
		LINK_NUMBERFORMAT_HASHNODE( 14 , "mm-dd-yy" )
		LINK_NUMBERFORMAT_HASHNODE( 15 , "d-mmm-yy" )
		LINK_NUMBERFORMAT_HASHNODE( 16 , "d-mmm" )
		LINK_NUMBERFORMAT_HASHNODE( 17 , "mmm-yy" )
		LINK_NUMBERFORMAT_HASHNODE( 18 , "h:mm AM/PM" )
		LINK_NUMBERFORMAT_HASHNODE( 19 , "h:mm:ss AM/PM" )
		LINK_NUMBERFORMAT_HASHNODE( 20 , "h:mm" )
		LINK_NUMBERFORMAT_HASHNODE( 21 , "h:mm:ss" )
		LINK_NUMBERFORMAT_HASHNODE( 22 , "m/d/yy h:mm" )
		LINK_NUMBERFORMAT_HASHNODE( 45 , "mm:ss" )
		LINK_NUMBERFORMAT_HASHNODE( 46 , "[h]:mm:ss" )
		LINK_NUMBERFORMAT_HASHNODE( 47 , "mmss.0" )
		LINK_NUMBERFORMAT_HASHNODE( 27 , "[$-404]e/m/d" )
		LINK_NUMBERFORMAT_HASHNODE( 30 , "m/d/yy" )
		LINK_NUMBERFORMAT_HASHNODE( 36 , "[$-404]e/m/d" )
		LINK_NUMBERFORMAT_HASHNODE( 50 , "[$-404]e/m/d" )
		LINK_NUMBERFORMAT_HASHNODE( 57 , "[$-404]e/m/d" )
		LINK_NUMBERFORMAT_HASHNODE( 67 , "t0%" )
		LINK_NUMBERFORMAT_HASHNODE( 68 , "t0.00%" )
	}
	else if( xpath_len == sizeof(XPATH_STYLESSHEET_NUMFMTS_NUMFMT_NUMFMTID)-1 && STRNCMP( xpath , == , XPATH_STYLESSHEET_NUMFMTS_NUMFMT_NUMFMTID , sizeof(XPATH_STYLESSHEET_NUMFMTS_NUMFMT_NUMFMTID)-1 ) )
	{
		if( msexcel_direct_prop->number_format == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "msexcel_direct_prop->number_format is null" )
			return -1;
		}
		
		msexcel_direct_prop->number_format->number_format_id = atol(propvalue) ;
		TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "number_format->number_format_id[%"PRIu64"]" , msexcel_direct_prop->number_format->number_format_id )
	}
	else if( xpath_len == sizeof(XPATH_STYLESSHEET_NUMFMTS_NUMFMT_FORMATCODE)-1 && STRNCMP( xpath , == , XPATH_STYLESSHEET_NUMFMTS_NUMFMT_FORMATCODE , sizeof(XPATH_STYLESSHEET_NUMFMTS_NUMFMT_FORMATCODE)-1 ) )
	{
		if( msexcel_direct_prop->number_format == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "msexcel_direct_prop->number_format is null" )
			return -1;
		}
		
		msexcel_direct_prop->number_format->number_format_code = strndup(propvalue,propvalue_len) ;
		TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "number_format->number_format_code[%s]" , msexcel_direct_prop->number_format->number_format_code )
	}
	else if( xpath_len == sizeof(XPATH_STYLESSHEET_CELLXFS_COUNT)-1 && STRNCMP( xpath , == , XPATH_STYLESSHEET_CELLXFS_COUNT , sizeof(XPATH_STYLESSHEET_CELLXFS_COUNT)-1 ) )
	{
		msexcel_direct_prop->xf_array_size = atol(propvalue) ;
		TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "xf_array_size[%"PRIu64"]" , msexcel_direct_prop->xf_array_size )
		msexcel_direct_prop->xf_array = (struct MsExcelStyleXf *)malloc( sizeof(struct MsExcelStyleXf) * msexcel_direct_prop->xf_array_size ) ;
		if( msexcel_direct_prop->xf_array == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "malloc failed , errno[%d]" , nret )
			return -1;
		}
		memset( msexcel_direct_prop->xf_array , 0x00 , sizeof(struct MsExcelStyleXf) * msexcel_direct_prop->xf_array_size );
		msexcel_direct_prop->xf_array_no = 0 ;
	}
	else if( xpath_len == sizeof(XPATH_STYLESSHEET_CELLXFS_XF_NUMFMTID)-1 && STRNCMP( xpath , == , XPATH_STYLESSHEET_CELLXFS_XF_NUMFMTID , sizeof(XPATH_STYLESSHEET_CELLXFS_XF_NUMFMTID)-1 ) )
	{
		if( msexcel_direct_prop->xf_array_no+1 > msexcel_direct_prop->xf_array_size )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "msexcel_direct_prop->xf_array_no[%"PRIu64"]+1 >= msexcel_direct_prop->xf_array_size[%"PRIu64"]" , msexcel_direct_prop->xf_array_no , msexcel_direct_prop->xf_array_size )
			return -1;
		}
		
		msexcel_direct_prop->xf_array[msexcel_direct_prop->xf_array_no].number_format_id = atol(propvalue) ;
		TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "set msexcel_direct_prop->xf_array[%"PRIu64"].number_format_id[%"PRIu64"]" , msexcel_direct_prop->xf_array_no , msexcel_direct_prop->xf_array[msexcel_direct_prop->xf_array_no].number_format_id )
		msexcel_direct_prop->xf_array_no++;
	}
	
	return 0;
}

#define XPATH_STYLESHEET_NUMFMTS	"/styleSheet/numFmts"
#define XPATH_STYLESHEET_NUMFMTS_NUMFMT	"/styleSheet/numFmts/numFmt"
#define XPATH_STYLESHEET_CELLXFS	"/styleSheet/cellXfs"
#define XPATH_STYLESHEET_CELLXFS_XF	"/styleSheet/cellXfs/xf"

static int CallbackOnStylesXmlNode( int type , char *xpath , int xpath_len , int xpath_size , char *node , int node_len , char *tag , int tag_len , char *properties , int properties_len , char *content , int content_len , void *user )
{
	struct ZlangDirectProperty_msexcel	*msexcel_direct_prop = (struct ZlangDirectProperty_msexcel *) user ;
	int					nret = 0 ;
	
	TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "XML - CallbackOnStylesXmlNode - type[0x%X] xpath[%.*s]" , type , xpath_len,xpath )
	
	if( type == (FASTERXML_NODE_ENTER|FASTERXML_NODE_BRANCH) && xpath_len == sizeof(XPATH_STYLESHEET_NUMFMTS)-1 && STRNCMP( xpath , == , XPATH_STYLESHEET_NUMFMTS , sizeof(XPATH_STYLESHEET_NUMFMTS)-1 ) )
	{
		nret = TravelXmlPropertiesBuffer( properties , properties_len , type , xpath , xpath_len , xpath_size , content , content_len , CallbackOnStylesXmlProperty , user ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "TravelXmlPropertiesBuffer failed[%d]" , nret )
			return nret;
		}
	}
	else if( type == FASTERXML_NODE_BRANCH && xpath_len == sizeof(XPATH_STYLESHEET_NUMFMTS_NUMFMT)-1 && STRNCMP( xpath , == , XPATH_STYLESHEET_NUMFMTS_NUMFMT , sizeof(XPATH_STYLESHEET_NUMFMTS_NUMFMT)-1 ) )
	{
		msexcel_direct_prop->number_format = (struct MsExcelStyleNumberFormat *)malloc( sizeof(struct MsExcelStyleNumberFormat) ) ;
		if( msexcel_direct_prop->number_format == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "malloc failed , errno[%d]" , errno )
			return nret;
		}
		memset( msexcel_direct_prop->number_format , 0x00 , sizeof(struct MsExcelStyleNumberFormat) );
		
		nret = TravelXmlPropertiesBuffer( properties , properties_len , type , xpath , xpath_len , xpath_size , content , content_len , CallbackOnStylesXmlProperty , user ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "TravelXmlPropertiesBuffer failed[%d]" , nret )
			return nret;
		}
		
		nret = LinkNumberFormatHashNode_BY_number_format_id( msexcel_direct_prop , msexcel_direct_prop->number_format ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "LinkNumberFormatHashNode_BY_number_format_id failed[%d]" , errno )
			DestroyNumberFormat( msexcel_direct_prop->number_format ); msexcel_direct_prop->number_format = NULL ;
			return nret;
		}
		else
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "LinkNumberFormatHashNode_BY_number_format_id ok , number_format_id[%"PRIu64"] number_format_code[%s]" , msexcel_direct_prop->number_format->number_format_id , msexcel_direct_prop->number_format->number_format_code )
		}
		
		msexcel_direct_prop->number_format = NULL ;
	}
	else if( type == (FASTERXML_NODE_ENTER|FASTERXML_NODE_BRANCH) && xpath_len == sizeof(XPATH_STYLESHEET_CELLXFS)-1 && STRNCMP( xpath , == , XPATH_STYLESHEET_CELLXFS , sizeof(XPATH_STYLESHEET_CELLXFS)-1 ) )
	{
		nret = TravelXmlPropertiesBuffer( properties , properties_len , type , xpath , xpath_len , xpath_size , content , content_len , CallbackOnStylesXmlProperty , user ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "TravelXmlPropertiesBuffer failed[%d]" , nret )
			return nret;
		}
	}
	else if( type == FASTERXML_NODE_BRANCH && xpath_len == sizeof(XPATH_STYLESHEET_CELLXFS_XF)-1 && STRNCMP( xpath , == , XPATH_STYLESHEET_CELLXFS_XF , sizeof(XPATH_STYLESHEET_CELLXFS_XF)-1 ) )
	{
		nret = TravelXmlPropertiesBuffer( properties , properties_len , type , xpath , xpath_len , xpath_size , content , content_len , CallbackOnStylesXmlProperty , user ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "TravelXmlPropertiesBuffer failed[%d]" , nret )
			return nret;
		}
	}
	
	return 0;
}

static int ParseStylesFileContent( struct ZlangDirectProperty_msexcel *msexcel_direct_prop , char *file_content , uint64_t file_content_len )
{
	char		xpath[ 1024 ] ;
	int		nret = 0 ;
	
	memset( xpath , 0x00 , sizeof(xpath) );
	nret = TravelXmlBuffer( file_content , xpath , sizeof(xpath) , CallbackOnStylesXmlNode , msexcel_direct_prop ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "TravelXmlBuffer failed[%d]" , nret )
		return nret;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "TravelXmlBuffer ok" )
		return 0;
	}
}

#define XPATH_XML_ENCODING			"/xml.encoding"
#define XPATH_WORKSHEET_DIMENSION_REF		"/worksheet/dimension.ref"
#define XPATH_WORKSHEET_SHEETDATA_ROW_C_R	"/worksheet/sheetData/row/c.r"
#define XPATH_WORKSHEET_SHEETDATA_ROW_C_S	"/worksheet/sheetData/row/c.s"
#define XPATH_WORKSHEET_SHEETDATA_ROW_C_T	"/worksheet/sheetData/row/c.t"

static int CallbackOnSheetXmlProperty( int type , char *xpath , int xpath_len , int xpath_size , char *propname , int propname_len , char *propvalue , int propvalue_len , char *content , int content_len , void *user )
{
	struct ZlangDirectProperty_msexcel	*msexcel_direct_prop = (struct ZlangDirectProperty_msexcel *) user ;
	char					*p = NULL ;
	int					nret = 0 ;
	
	TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "XML - CallbackOnSheetXmlProperty - type[0x%X] xpath[%.*s]" , type , xpath_len,xpath )
	
	if( xpath_len == sizeof(XPATH_XML_ENCODING)-1 && STRNCMP( xpath , == , XPATH_XML_ENCODING , sizeof(XPATH_XML_ENCODING)-1 ) )
	{
		msexcel_direct_prop->p_sheet->file_encoding = strndup( propvalue , propvalue_len ) ;
		if( msexcel_direct_prop->p_sheet->file_encoding == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "malloc failed , errno[%d]" , errno )
			return -1;
		}
		TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "get file encoding[%s]" , msexcel_direct_prop->p_sheet->file_encoding )
	}
	else if( xpath_len == sizeof(XPATH_WORKSHEET_DIMENSION_REF)-1 && STRNCMP( xpath , == , XPATH_WORKSHEET_DIMENSION_REF , sizeof(XPATH_WORKSHEET_DIMENSION_REF)-1 ) )
	{
		p = strchr( propvalue , ':' ) ;
		if( p == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "[%s][%.*s] invalid" , XPATH_WORKSHEET_DIMENSION_REF , propvalue_len,propvalue )
			return -11;
		}
		p++;
		
		nret = CellLabelToPosition( p , & (msexcel_direct_prop->p_sheet->col_count) , & (msexcel_direct_prop->p_sheet->row_count) ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "[%s][%.*s] invalid[%d]" , XPATH_WORKSHEET_DIMENSION_REF , propvalue_len,propvalue , nret )
			return -12;
		}
		TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "col_count[%"PRIu64"] row_count[%"PRIu64"]" , msexcel_direct_prop->p_sheet->col_count , msexcel_direct_prop->p_sheet->row_count )
		
		msexcel_direct_prop->p_sheet->matrix = (struct MsExcelCell *)malloc( sizeof(struct MsExcelCell) * msexcel_direct_prop->p_sheet->col_count*msexcel_direct_prop->p_sheet->row_count ) ;
		if( msexcel_direct_prop->p_sheet->matrix == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "malloc failed , errno[%d]" , nret )
			return -13;
		}
		memset( msexcel_direct_prop->p_sheet->matrix , 0x00 , sizeof(struct MsExcelCell) * msexcel_direct_prop->p_sheet->col_count*msexcel_direct_prop->p_sheet->row_count );
	}
	else if( xpath_len == sizeof(XPATH_WORKSHEET_SHEETDATA_ROW_C_R)-1 && STRNCMP( xpath , == , XPATH_WORKSHEET_SHEETDATA_ROW_C_R , sizeof(XPATH_WORKSHEET_SHEETDATA_ROW_C_R)-1 ) )
	{
		uint64_t		col_no ;
		uint64_t		row_no ;
		
		nret = CellLabelToPosition( propvalue , & col_no , & row_no ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "[%s][%.*s] invalid[%d]" , XPATH_WORKSHEET_DIMENSION_REF , propvalue_len,propvalue , nret )
			return -21;
		}
		TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "locate col_no[%"PRIu64"] row_no[%"PRIu64"]" , col_no , row_no )
		
		if( msexcel_direct_prop->p_sheet->matrix == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "msexcel_direct_prop->p_sheet->matrix is null" )
			return -22;
		}
		
		msexcel_direct_prop->p_sheet->p_cell = msexcel_direct_prop->p_sheet->matrix + (row_no-1) * msexcel_direct_prop->p_sheet->col_count + (col_no-1) ;
	}
	else if( xpath_len == sizeof(XPATH_WORKSHEET_SHEETDATA_ROW_C_S)-1 && STRNCMP( xpath , == , XPATH_WORKSHEET_SHEETDATA_ROW_C_S , sizeof(XPATH_WORKSHEET_SHEETDATA_ROW_C_S)-1 ) )
	{
		if( msexcel_direct_prop->p_sheet->p_cell == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "msexcel_direct_prop->p_cell invalid" )
			return -1;
		}
		
		msexcel_direct_prop->p_sheet->p_cell->s = strndup( propvalue , propvalue_len ) ;
		msexcel_direct_prop->p_sheet->p_cell->s_len = propvalue_len ;
	}
	else if( xpath_len == sizeof(XPATH_WORKSHEET_SHEETDATA_ROW_C_T)-1 && STRNCMP( xpath , == , XPATH_WORKSHEET_SHEETDATA_ROW_C_T , sizeof(XPATH_WORKSHEET_SHEETDATA_ROW_C_T)-1 ) )
	{
		if( msexcel_direct_prop->p_sheet->p_cell == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "msexcel_direct_prop->p_cell invalid" )
			return -1;
		}
		
		msexcel_direct_prop->p_sheet->p_cell->t = strndup( propvalue , propvalue_len ) ;
		msexcel_direct_prop->p_sheet->p_cell->t_len = propvalue_len ;
	}
	
	return 0;
}

#define XPATH_XML				"/xml"
#define XPATH_WORKSHEET_DIMENSION		"/worksheet/dimension"
#define XPATH_WORKSHEET_SHEETDATA_ROW_C		"/worksheet/sheetData/row/c"
#define XPATH_WORKSHEET_SHEETDATA_ROW_C_V	"/worksheet/sheetData/row/c/v"

static int CallbackOnSheetXmlNode( int type , char *xpath , int xpath_len , int xpath_size , char *node , int node_len , char *tag , int tag_len , char *properties , int properties_len , char *content , int content_len , void *user )
{
	struct ZlangDirectProperty_msexcel	*msexcel_direct_prop = (struct ZlangDirectProperty_msexcel *) user ;
	int					nret = 0 ;
	
	TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "XML - CallbackOnSheetXmlNode - type[0x%X] xpath[%.*s]" , type , xpath_len,xpath )
	
	if( type == FASTERXML_NODE_BRANCH && xpath_len == sizeof(XPATH_XML)-1 && STRNCMP( xpath , == , XPATH_XML , sizeof(XPATH_XML)-1 ) )
	{
		nret = TravelXmlPropertiesBuffer( properties , properties_len , type , xpath , xpath_len , xpath_size , content , content_len , CallbackOnSheetXmlProperty , user ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "TravelXmlPropertiesBuffer failed[%d]" , nret )
			return nret;
		}
	}
	else if( type == FASTERXML_NODE_BRANCH && xpath_len == sizeof(XPATH_WORKSHEET_DIMENSION)-1 && STRNCMP( xpath , == , XPATH_WORKSHEET_DIMENSION , sizeof(XPATH_WORKSHEET_DIMENSION)-1 ) )
	{
		nret = TravelXmlPropertiesBuffer( properties , properties_len , type , xpath , xpath_len , xpath_size , content , content_len , CallbackOnSheetXmlProperty , user ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "TravelXmlPropertiesBuffer failed[%d]" , nret )
			return nret;
		}
	}
	else if( type == (FASTERXML_NODE_ENTER|FASTERXML_NODE_BRANCH) && xpath_len == sizeof(XPATH_WORKSHEET_SHEETDATA_ROW_C)-1 && STRNCMP( xpath , == , XPATH_WORKSHEET_SHEETDATA_ROW_C , sizeof(XPATH_WORKSHEET_SHEETDATA_ROW_C)-1 ) )
	{
		nret = TravelXmlPropertiesBuffer( properties , properties_len , type , xpath , xpath_len , xpath_size , content , content_len , CallbackOnSheetXmlProperty , user ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "TravelXmlPropertiesBuffer failed[%d]" , nret )
			return nret;
		}
	}
	else if( type == FASTERXML_NODE_LEAF && xpath_len == sizeof(XPATH_WORKSHEET_SHEETDATA_ROW_C_V)-1 && STRNCMP( xpath , == , XPATH_WORKSHEET_SHEETDATA_ROW_C_V , sizeof(XPATH_WORKSHEET_SHEETDATA_ROW_C_V)-1 ) )
	{
		if( msexcel_direct_prop->p_sheet->p_cell == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "msexcel_direct_prop->p_cell invalid" )
			return -1;
		}
		
		msexcel_direct_prop->p_sheet->p_cell->v = strndup( content , content_len ) ;
		msexcel_direct_prop->p_sheet->p_cell->v_len = content_len ;
		
		msexcel_direct_prop->p_sheet->p_cell = NULL ;
	}
	
	return 0;
}

static int ParseSheetFileContent( struct ZlangDirectProperty_msexcel *msexcel_direct_prop , char *file_content , uint64_t file_content_len )
{
	char		xpath[ 1024 ] ;
	int		nret = 0 ;
	
	memset( xpath , 0x00 , sizeof(xpath) );
	nret = TravelXmlBuffer( file_content , xpath , sizeof(xpath) , CallbackOnSheetXmlNode , msexcel_direct_prop ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "TravelXmlBuffer failed[%d]" , nret )
		return nret;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( msexcel_direct_prop->rt , "TravelXmlBuffer ok" )
		return 0;
	}
}

#define WORKBOOK_FILENAME	"xl/workbook.xml"
#define SHAREDSTRINGS_FILENAME	"xl/sharedStrings.xml"
#define STYLES_FILENAME		"xl/styles.xml"
#define SHEET_FILENAME_PREFIX	"xl/worksheets/"

ZlangInvokeFunction ZlangInvokeFunction_msexcel_LoadFile_string;
int ZlangInvokeFunction_msexcel_LoadFile_string( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_msexcel	*msexcel_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*in1 = GetInputParameterInLocalObjectStack(rt,1) ;
	struct ZlangObject			*out1 = GetOutputParameterInLocalObjectStack(rt,1) ;
	unz_global_info				global_info ;
	unz_file_info				file_info ;
	uint64_t				number_entry ;
	int					i ;
	char					filename[ PATH_MAX ] ;
	char					*extname = NULL ;
	char					*buf = NULL ;
	uint64_t				buf_size ;
	uint64_t				buf_len ;
	
	struct MsExcelSheet			*p_sheet = NULL ;
	struct MsExcelWorkbookSheet		a_workbook_sheet ;
	struct MsExcelWorkbookSheet		*p_workbook_sheet = NULL ;
	
	int					nret = 0 ;
	
	CallRuntimeFunction_string_ExpandEnvVariables( rt , in1 );
	CallRuntimeFunction_string_GetStringValue( rt , in1 , & (msexcel_direct_prop->msexcel_filename) , NULL );
	
	msexcel_direct_prop->unzip_file = unzOpen( msexcel_direct_prop->msexcel_filename ) ;
	if( msexcel_direct_prop->unzip_file == NULL )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "unzOpen[%s] failed" , msexcel_direct_prop->msexcel_filename )
		msexcel_direct_prop->msexcel_filename = NULL ;
		CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
		return 0;
	}
	
	nret = unzGetGlobalInfo( msexcel_direct_prop->unzip_file , & global_info ) ;
	if( nret != UNZ_OK )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "unzGetGlobalInfo[%s] failed" , msexcel_direct_prop->msexcel_filename )
		unzClose( msexcel_direct_prop->unzip_file );
		msexcel_direct_prop->msexcel_filename = NULL ;
		CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
		return 0;
	}
	
	number_entry = global_info.number_entry ;
	
	for( i = 0 ; i < number_entry ; i++ )
	{
		memset( filename , 0x00 , sizeof(filename) );
		nret = unzGetCurrentFileInfo( msexcel_direct_prop->unzip_file , & file_info , filename , sizeof(filename) , NULL , 0 , NULL , 0 ) ;
		if( nret != UNZ_OK )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "unzGetCurrentFileInfo[%s] failed" , msexcel_direct_prop->msexcel_filename )
			if( buf ) free( buf );
			msexcel_direct_prop->msexcel_filename = NULL ;
			unzClose( msexcel_direct_prop->unzip_file );
			CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
			return 0;
		}
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "FILE-IN-ZIP - file_info.external_fa[%lu] filename[%s]" , (unsigned long)(file_info.external_fa) , filename )
		
		extname = strchr( filename , '.' ) ;
		if( extname )
			extname++;
		else
			extname = "" ;
		
		if( STRCMP( filename , == , WORKBOOK_FILENAME ) )
		{
			nret = OpenAndReadAndCloseFileInZip( msexcel_direct_prop->unzip_file , & file_info , filename , & buf , & buf_size , & buf_len ) ;
			if( nret )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "OpenAndReadAndCloseFileInZip[%s] failed[%d]" , msexcel_direct_prop->msexcel_filename , nret )
				if( buf ) free( buf );
				msexcel_direct_prop->msexcel_filename = NULL ;
				unzClose( msexcel_direct_prop->unzip_file );
				CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
				return 0;
			}
			
			nret = ParseWorkbookFileContent( msexcel_direct_prop , buf , buf_len ) ;
			if( nret )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ParseWorkbookFileContent[%s][%s] failed[%d]" , msexcel_direct_prop->msexcel_filename , filename , nret )
				if( buf ) free( buf );
				msexcel_direct_prop->msexcel_filename = NULL ;
				unzClose( msexcel_direct_prop->unzip_file );
				CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
				return 0;
			}
		}
		else if( STRCMP( filename , == , SHAREDSTRINGS_FILENAME ) )
		{
			nret = OpenAndReadAndCloseFileInZip( msexcel_direct_prop->unzip_file , & file_info , filename , & buf , & buf_size , & buf_len ) ;
			if( nret )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "OpenAndReadAndCloseFileInZip[%s] failed[%d]" , msexcel_direct_prop->msexcel_filename , nret )
				if( buf ) free( buf );
				msexcel_direct_prop->msexcel_filename = NULL ;
				unzClose( msexcel_direct_prop->unzip_file );
				CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
				return 0;
			}
			
			nret = ParseSharedStringsFileContent( msexcel_direct_prop , buf , buf_len ) ;
			if( nret )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ParseSharedStringsFileContent[%s][%s] failed[%d]" , msexcel_direct_prop->msexcel_filename , filename , nret )
				if( buf ) free( buf );
				msexcel_direct_prop->msexcel_filename = NULL ;
				unzClose( msexcel_direct_prop->unzip_file );
				CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
				return 0;
			}
		}
		else if( STRCMP( filename , == , STYLES_FILENAME ) )
		{
			nret = OpenAndReadAndCloseFileInZip( msexcel_direct_prop->unzip_file , & file_info , filename , & buf , & buf_size , & buf_len ) ;
			if( nret )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "OpenAndReadAndCloseFileInZip[%s] failed[%d]" , msexcel_direct_prop->msexcel_filename , nret )
				if( buf ) free( buf );
				msexcel_direct_prop->msexcel_filename = NULL ;
				unzClose( msexcel_direct_prop->unzip_file );
				CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
				return 0;
			}
			
			nret = ParseStylesFileContent( msexcel_direct_prop , buf , buf_len ) ;
			if( nret )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ParseStylesFileContent[%s][%s] failed[%d]" , msexcel_direct_prop->msexcel_filename , filename , nret )
				if( buf ) free( buf );
				msexcel_direct_prop->msexcel_filename = NULL ;
				unzClose( msexcel_direct_prop->unzip_file );
				CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
				return 0;
			}
		}
		else if( STRNCMP( filename , == , SHEET_FILENAME_PREFIX , sizeof(SHEET_FILENAME_PREFIX)-1 ) && STRCMP( extname , == , "xml" ) )
		{
			char	*p = NULL ;
			
			msexcel_direct_prop->p_sheet = (struct MsExcelSheet *)malloc( sizeof(struct MsExcelSheet) ) ;
			if( msexcel_direct_prop->p_sheet == NULL )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "alloc failed , errno[%d]" , errno )
				if( buf ) free( buf );
				msexcel_direct_prop->msexcel_filename = NULL ;
				unzClose( msexcel_direct_prop->unzip_file );
				CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
				return 0;
			}
			memset( msexcel_direct_prop->p_sheet , 0x00 , sizeof(struct MsExcelSheet) );
			
			p = strchr( filename+sizeof(SHEET_FILENAME_PREFIX)-1 , '.' ) ;
			if( p == NULL )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "sheet filename[%s] invalid" , filename )
				if( buf ) free( buf );
				DestroyMsSheet( msexcel_direct_prop->p_sheet ); msexcel_direct_prop->p_sheet = NULL ;
				msexcel_direct_prop->msexcel_filename = NULL ;
				unzClose( msexcel_direct_prop->unzip_file );
				CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
				return 0;
			}
			msexcel_direct_prop->p_sheet->sheet_filename = strndup( filename+sizeof(SHEET_FILENAME_PREFIX)-1 , p - (filename+sizeof(SHEET_FILENAME_PREFIX)-1) );
			
			nret = OpenAndReadAndCloseFileInZip( msexcel_direct_prop->unzip_file , & file_info , filename , & buf , & buf_size , & buf_len ) ;
			if( nret )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "OpenAndReadAndCloseFileInZip[%s] failed[%d]" , msexcel_direct_prop->msexcel_filename , nret )
				if( buf ) free( buf );
				DestroyMsSheet( msexcel_direct_prop->p_sheet ); msexcel_direct_prop->p_sheet = NULL ;
				msexcel_direct_prop->msexcel_filename = NULL ;
				unzClose( msexcel_direct_prop->unzip_file );
				CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
				return 0;
			}
			
			nret = ParseSheetFileContent( msexcel_direct_prop , buf , buf_len ) ;
			if( nret )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ParseSheetFileContent[%s][%s] failed[%d]" , msexcel_direct_prop->msexcel_filename , filename , nret )
				if( buf ) free( buf );
				DestroyMsSheet( msexcel_direct_prop->p_sheet ); msexcel_direct_prop->p_sheet = NULL ;
				msexcel_direct_prop->msexcel_filename = NULL ;
				unzClose( msexcel_direct_prop->unzip_file );
				CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
				return 0;
			}
			
			list_add_tail( & (msexcel_direct_prop->p_sheet->sheet_list_node) , & (msexcel_direct_prop->sheet_list) );
			msexcel_direct_prop->p_sheet = NULL ;
		}
		
		unzGoToNextFile( msexcel_direct_prop->unzip_file );
	}
	
	memset( & a_workbook_sheet , 0x00 , sizeof(struct MsExcelWorkbookSheet) );
	list_for_each_entry( p_sheet , & (msexcel_direct_prop->sheet_list) , struct MsExcelSheet , sheet_list_node )
	{
		a_workbook_sheet.sheet_id = atol(p_sheet->sheet_filename+5) ;
		p_workbook_sheet = QueryWorkbookSheetTreeBySheetId( msexcel_direct_prop , & a_workbook_sheet ) ;
		if( p_workbook_sheet == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "QueryWorkbookSheetTreeBySheetId failed" )
			unzClose( msexcel_direct_prop->unzip_file ); msexcel_direct_prop->unzip_file = NULL ;
			CallRuntimeFunction_bool_SetBoolValue( rt , out1 , TRUE );
			CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
			return 0;
		}
		
		p_sheet->sheet_name = strdup( p_workbook_sheet->sheet_name ) ;
		if( p_sheet->sheet_name == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "strdup failed , errno[%d]" , errno )
			unzClose( msexcel_direct_prop->unzip_file ); msexcel_direct_prop->unzip_file = NULL ;
			CallRuntimeFunction_bool_SetBoolValue( rt , out1 , TRUE );
			CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
			return 0;
		}
	}
	
	if( buf ) free( buf );
	unzClose( msexcel_direct_prop->unzip_file ); msexcel_direct_prop->unzip_file = NULL ;
	CallRuntimeFunction_bool_SetBoolValue( rt , out1 , TRUE );
	
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_msexcel_SetConvertEncoding_string;
int ZlangInvokeFunction_msexcel_SetConvertEncoding_string( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_msexcel	*msexcel_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*in1 = GetInputParameterInLocalObjectStack(rt,1) ;
	struct ZlangObject			*out1 = GetOutputParameterInLocalObjectStack(rt,1) ;
	char					*convert_encoding = NULL ;
	int32_t					convert_encoding_len ;
	
	CallRuntimeFunction_string_GetStringValue( rt , in1 , & convert_encoding , & convert_encoding_len );
	
	msexcel_direct_prop->convert_encoding = strndup( convert_encoding , convert_encoding_len ) ;
	if( msexcel_direct_prop->convert_encoding == NULL )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "strndup failed , errno[%d]" , errno )
		UnreferObject( rt , out1 );
		return 0;
	}
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "set output encoding[%s]" , msexcel_direct_prop->convert_encoding )
	
	CallRuntimeFunction_bool_SetBoolValue( rt , out1 , TRUE );
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_msexcel_GetSheetList;
int ZlangInvokeFunction_msexcel_GetSheetList( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_msexcel	*msexcel_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*out1 = GetOutputParameterInLocalObjectStack(rt,1) ;
	struct MsExcelSheet			*p_sheet = NULL ;
	struct ZlangObject			*str_obj = NULL ;
	int					nret = 0 ;
	
	list_for_each_entry( p_sheet , & (msexcel_direct_prop->sheet_list) , struct MsExcelSheet , sheet_list_node )
	{
		str_obj = CloneStringObjectInTmpStack( rt , NULL ) ;
		if( str_obj == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CloneStringObjectInTmpStack failed" )
			UnreferObject( rt , out1 );
			return 0;
		}
		
		nret = CallRuntimeFunction_string_SetStringValue( rt , str_obj , p_sheet->sheet_name , -1 ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CallRuntimeFunction_string_SetStringValue failed[%d]" , nret )
			UnreferObject( rt , out1 );
			return 0;
		}
		
		nret = CallRuntimeFunction_list_AddTail( rt , out1 , str_obj , NULL ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CallRuntimeFunction_list_AddTail failed[%d]" , nret )
			UnreferObject( rt , out1 );
			return 0;
		}
	}
	
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_msexcel_SelectSheet_string;
int ZlangInvokeFunction_msexcel_SelectSheet_string( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_msexcel	*msexcel_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*in1 = GetInputParameterInLocalObjectStack(rt,1) ;
	struct ZlangObject			*out1 = GetOutputParameterInLocalObjectStack(rt,1) ;
	char					*sheet_name = NULL ;
	struct MsExcelSheet			*p_sheet = NULL ;
	
	CallRuntimeFunction_string_GetStringValue( rt , in1 , & sheet_name , NULL );
	
	msexcel_direct_prop->p_sheet = NULL ;
	list_for_each_entry( p_sheet , & (msexcel_direct_prop->sheet_list) , struct MsExcelSheet , sheet_list_node )
	{
		if( STRCMP( p_sheet->sheet_name , == , sheet_name ) )
		{
			msexcel_direct_prop->p_sheet = p_sheet ;
			break;
		}
	}
	if( msexcel_direct_prop->p_sheet )
		CallRuntimeFunction_bool_SetBoolValue( rt , out1 , TRUE );
	else
		CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
	
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_msexcel_GetRowCount;
int ZlangInvokeFunction_msexcel_GetRowCount( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_msexcel	*msexcel_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*out1 = GetOutputParameterInLocalObjectStack(rt,1) ;
	int32_t					row_count ;
	
	if( msexcel_direct_prop->p_sheet == NULL )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "sheet not selected" )
		UnreferObject( rt , out1 );
		return 0;
	}
	
	row_count = msexcel_direct_prop->p_sheet->row_count ;
	CallRuntimeFunction_int_SetIntValue( rt , out1 , row_count );
	
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_msexcel_GetColumnCount;
int ZlangInvokeFunction_msexcel_GetColumnCount( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_msexcel	*msexcel_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*out1 = GetOutputParameterInLocalObjectStack(rt,1) ;
	int32_t					col_count ;
	
	if( msexcel_direct_prop->p_sheet == NULL )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "sheet not selected" )
		UnreferObject( rt , out1 );
		return 0;
	}
	
	col_count = msexcel_direct_prop->p_sheet->col_count ;
	CallRuntimeFunction_int_SetIntValue( rt , out1 , col_count );
	
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_msexcel_GetCellString_int_int;
int ZlangInvokeFunction_msexcel_GetCellString_int_int( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_msexcel	*msexcel_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*in1 = GetInputParameterInLocalObjectStack(rt,1) ;
	struct ZlangObject			*in2 = GetInputParameterInLocalObjectStack(rt,2) ;
	struct ZlangObject			*out1 = GetOutputParameterInLocalObjectStack(rt,1) ;
	int32_t					row_no ;
	int32_t					col_no ;
	struct MsExcelCell			*p_cell = NULL ;
	struct MsExcelSharedString		*p_shared_strings_ptr = NULL ;
	char					*p_str = NULL ;
	size_t					str_len ;
	int					nret = 0 ;
	
	CallRuntimeFunction_int_GetIntValue( rt , in1 , & row_no );
	CallRuntimeFunction_int_GetIntValue( rt , in2 , & col_no );
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "cell row_no[%"PRIi32"] col_no[%"PRIi32"]" , row_no , col_no )
	
	if( msexcel_direct_prop->p_sheet == NULL )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "sheet not selected" )
		UnreferObject( rt , out1 );
		return 0;
	}
	if( row_no > msexcel_direct_prop->p_sheet->row_count )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "row_no[%"PRIi32"] too big , row_count[%"PRIu64"]" , row_no , msexcel_direct_prop->p_sheet->row_count )
		UnreferObject( rt , out1 );
		return 0;
	}
	if( col_no > msexcel_direct_prop->p_sheet->col_count )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "col_no[%"PRIi32"] too big , col_count[%"PRIu64"]" , col_no , msexcel_direct_prop->p_sheet->col_count )
		UnreferObject( rt , out1 );
		return 0;
	}
	
#define CELL_T_S	"s"
	p_cell = msexcel_direct_prop->p_sheet->matrix + (row_no-1) * msexcel_direct_prop->p_sheet->col_count + (col_no-1) ;
	if( p_cell->t_len == sizeof(CELL_T_S)-1 && STRNCMP( p_cell->t , == , CELL_T_S , sizeof(CELL_T_S)-1 ) )
	{
		p_shared_strings_ptr = msexcel_direct_prop->shared_strings + atol(p_cell->v) ;
		p_str = p_shared_strings_ptr->t ;
		str_len = p_shared_strings_ptr->t_len ;
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "t[%.*s] : [%.*s]" , (int)(p_cell->t_len),p_cell->t , (int)str_len,p_str )
	}
	else if( p_cell->s && msexcel_direct_prop->number_format_hashmap_size > 0 )
	{
		uint64_t			style_index ;
		struct MsExcelStyleXf		*p_xf = NULL ;
		struct MsExcelStyleNumberFormat	a_number_format ;
		struct MsExcelStyleNumberFormat	*p_number_format = NULL ;
		char				*p_yy = NULL ;
		char				*p_h = NULL ;
		char				*p_percent = NULL ;
		
		style_index = atol(p_cell->s) ;
		p_xf = msexcel_direct_prop->xf_array+style_index ;
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "p_cell->s[%.*s] -> style_index[%"PRIu64"] -> p_xf[%p]->number_format_id[%"PRIu64"]" , (int)(p_cell->s_len),p_cell->s , style_index , p_xf , p_xf->number_format_id )
		
		memset( & a_number_format , 0x00 , sizeof(struct MsExcelStyleNumberFormat) );
		a_number_format.number_format_id = p_xf->number_format_id ;
		p_number_format = QueryNumberFormatHashNode_BY_number_format_id( msexcel_direct_prop , & a_number_format ) ;
		if( p_number_format )
		{
			p_yy = strstr( p_number_format->number_format_code , "yy" ) ;
			p_h = strstr( p_number_format->number_format_code , "h" ) ;
			p_percent = strchr( p_number_format->number_format_code , '%' ) ;
			if( p_yy && p_h == NULL )
			{
				unsigned int	days ;
				unsigned int	year , month , day ;
				char		date_buf[ 4+1+2+1+2 + 1 ] ;
				
				days = atol(p_cell->v) ;
				
				nret = MsExcel_DaysToDateSince1900_01_01( days , & year , & month , & day ) ;
				if( nret )
				{
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "MsExcel_DaysToDateSince1900_01_01[%.*s] failed[%d]" , (int)(p_cell->v_len),p_cell->v , nret )
					UnreferObject( rt , out1 );
					return 0;
				}
				
				memset( date_buf , 0x00 , sizeof(date_buf) );
				str_len = snprintf( date_buf , sizeof(date_buf) , "%04d-%02d-%02d" , year , month , day ) ;
				p_str = date_buf ;
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "s(DATE)[%s][%.*s] : [%.*s]" , p_number_format->number_format_code , (int)(p_cell->v_len),p_cell->v , (int)str_len,p_str )
			}
			else if( p_yy == NULL && p_h )
			{
				double	precent_of_seconds_in_oneday ;
				long	seconds_in_oneday ;
				int	hour , minute , second ;
				char	time_buf[ 2+1+2+1+2 + 1 ] ;
				
				precent_of_seconds_in_oneday = strtod( p_cell->v , NULL ) ;
				
				seconds_in_oneday = (24*60*60) * precent_of_seconds_in_oneday ;
				minute = seconds_in_oneday / 60 ;
				second = seconds_in_oneday % 60 ;
				hour = minute / 60 ;
				minute = minute % 60 ;
				
				memset( time_buf , 0x00 , sizeof(time_buf) );
				str_len = snprintf( time_buf , sizeof(time_buf) , "%02d:%02d:%02d" , hour , minute , second ) ;
				p_str = time_buf ;
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "s(TIME)[%s][%.*s] : [%.*s]" , p_number_format->number_format_code , (int)(p_cell->v_len),p_cell->v , (int)str_len,p_str )
			}
			else if( p_yy && p_h )
			{
				char		*p = NULL ;
				unsigned int	days ;
				unsigned int	year , month , day ;
				double		precent_of_seconds_in_oneday ;
				long		seconds_in_oneday ;
				int		hour , minute , second ;
				char		datetime_buf[ 4+1+2+1+2 + 1 + 2+1+2+1+2 + 1 ] ;
				
				days = atol(p_cell->v) ;
				p = strchr( p_cell->v , '.' ) ;
				if( p )
				{
					precent_of_seconds_in_oneday = strtod( p , NULL ) + 0.00001 ;
				}
				else
				{
					precent_of_seconds_in_oneday = 0.00 ;
				}
				
				nret = MsExcel_DaysToDateSince1900_01_01( days , & year , & month , & day ) ;
				if( nret )
				{
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "MsExcel_DaysToDateSince1900_01_01[%.*s] failed[%d]" , (int)(p_cell->v_len),p_cell->v , nret )
					UnreferObject( rt , out1 );
					return 0;
				}
				
				seconds_in_oneday = (24*60*60) * precent_of_seconds_in_oneday ;
				minute = seconds_in_oneday / 60 ;
				second = seconds_in_oneday % 60 ;
				hour = minute / 60 ;
				minute = minute % 60 ;
				
				memset( datetime_buf , 0x00 , sizeof(datetime_buf) );
				str_len = snprintf( datetime_buf , sizeof(datetime_buf) , "%04d-%02d-%02d %02d:%02d:%02d" , year , month , day , hour , minute , second ) ;
				p_str = datetime_buf ;
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "s(DATETIME)[%s][%.*s] : [%.*s]" , p_number_format->number_format_code , (int)(p_cell->v_len),p_cell->v , (int)str_len,p_str )
			}
			else if( p_percent )
			{
				char	*p = NULL ;
				int	decimal ;
				char	format[ 10 ] ;
				char	buf[ 30 ] ;
				
				for( decimal = 0 , p = p_percent-1 ; p >= p_number_format->number_format_code ; decimal++ , p-- )
				{
					if( (*p) != '0' )
						break;
				}
				
				memset( format , 0x00 , sizeof(format) );
				snprintf( format , sizeof(format) , "%%.%dlf%%%%" , decimal );
				memset( buf , 0x00 , sizeof(buf) );
				str_len = snprintf( buf , sizeof(buf)-1 , format , atof(p_cell->v)*100 ) ;
				p_str = buf ;
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "s(PERCENT)[%s][%s] : [%.*s]" , p_number_format->number_format_code , format , (int)str_len,p_str )
			}
			else
			{
#if 0
#define FROM_GENERAL_STRING \
				if( msexcel_direct_prop->convert_encoding == NULL || msexcel_direct_prop->p_sheet->file_encoding == NULL ) \
				{ \
				} \
				else \
				{ \
					nret = ConvertStringEncoding( msexcel_direct_prop->p_sheet->file_encoding , msexcel_direct_prop->convert_encoding , p_cell->v , (size_t)(p_cell->v_len) , & p_str , NULL , & str_len ) ; \
					if( nret ) \
					{ \
						TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ConvertStringEncoding failed[%d] , in[%p] in_len[%"PRIi32"]" , nret , p_shared_strings_ptr->t , p_shared_strings_ptr->t_len ) \
						UnreferObject( rt , out1 ); \
						return 0; \
					} \
					p_alloced = p_str ; \
				}
#endif
				p_str = p_cell->v ;
				str_len = p_cell->v_len ;
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "number_format_code[%s] : [%.*s]" , p_number_format->number_format_code , (int)str_len,p_str )
			}
		}
		else
		{
			p_str = p_cell->v ;
			str_len = p_cell->v_len ;
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "number_format_id[%"PRIu64"] not found : [%.*s]" , a_number_format.number_format_id , (int)str_len,p_str )
		}
	}
	else
	{
		p_str = p_cell->v ;
		str_len = p_cell->v_len ;
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "t[%.*s] : [%.*s]" , (int)(p_cell->t_len),p_cell->t , (int)str_len,p_str )
	}
	
	if( p_str == NULL )
		p_str = "" ;
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "get cell[%"PRIi32"][%"PRIi32"] string[%.*s]" , row_no , col_no , (int)str_len,p_str )
	CallRuntimeFunction_string_SetStringValue( rt , out1 , p_str , str_len );
	
	return 0;
}

ZlangCreateDirectPropertyFunction ZlangCreateDirectProperty_msexcel;
void *ZlangCreateDirectProperty_msexcel( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_msexcel	*msexcel_prop = NULL ;
	
	msexcel_prop = (struct ZlangDirectProperty_msexcel *)ZLMALLOC( sizeof(struct ZlangDirectProperty_msexcel) ) ;
	if( msexcel_prop == NULL )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_ALLOC , "alloc memory for entity" )
		return NULL;
	}
	memset( msexcel_prop , 0x00 , sizeof(struct ZlangDirectProperty_msexcel) );
	
	msexcel_prop->rt = rt ;
	INIT_LIST_HEAD( & (msexcel_prop->sheet_list) );
	
	return msexcel_prop;
}

ZlangDestroyDirectPropertyFunction ZlangDestroyDirectProperty_msexcel;
void ZlangDestroyDirectProperty_msexcel( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_msexcel	*msexcel_direct_prop = GetObjectDirectProperty(obj) ;
	struct MsExcelSharedString		*shared_strings_ptr = NULL ;
	struct MsExcelSharedString		*shared_strings_end_over = msexcel_direct_prop->shared_strings + msexcel_direct_prop->shared_strings_count ;
	struct MsExcelSheet			*p_sheet = NULL ;
	struct MsExcelSheet			*p_next_sheet = NULL ;
	
	msexcel_direct_prop->msexcel_filename = NULL ;
	
	if( msexcel_direct_prop->convert_encoding )
	{
		free( msexcel_direct_prop->convert_encoding );
	}
	
	if( msexcel_direct_prop->workbook_encoding )
	{
		free( msexcel_direct_prop->workbook_encoding );
	}
	
	DestroyWorkbookSheetTree( msexcel_direct_prop );
	
	if( msexcel_direct_prop->shared_strings_encoding )
	{
		free( msexcel_direct_prop->shared_strings_encoding );
	}
	
	if( msexcel_direct_prop->shared_strings )
	{
		for( shared_strings_ptr = msexcel_direct_prop->shared_strings ; shared_strings_ptr < shared_strings_end_over ; shared_strings_ptr++ )
		{
			if( shared_strings_ptr->t )
				free( shared_strings_ptr->t );
		}
		free( msexcel_direct_prop->shared_strings );
	}
	
	if( msexcel_direct_prop->number_format_hashmap )
	{
		DestroyNumberFormatHashMap( msexcel_direct_prop );
	}
	
	if( msexcel_direct_prop->xf_array )
	{
		free( msexcel_direct_prop->xf_array );
	}
	
	list_for_each_entry_safe( p_sheet , p_next_sheet , & (msexcel_direct_prop->sheet_list) , struct MsExcelSheet , sheet_list_node )
	{
		list_del( & (p_sheet->sheet_list_node) );
		
		DestroyMsSheet( p_sheet );
	}
	
	ZLFREE( msexcel_direct_prop );
	
	return;
}

ZlangSummarizeDirectPropertySizeFunction ZlangSummarizeDirectPropertySize_msexcel;
void ZlangSummarizeDirectPropertySize_msexcel( struct ZlangRuntime *rt , struct ZlangObject *obj , size_t *summarized_obj_size , size_t *summarized_direct_prop_size )
{
	SUMMARIZE_SIZE( summarized_direct_prop_size , sizeof(struct ZlangDirectProperty_msexcel) )
	return;
}

static struct ZlangDirectFunctions direct_funcs_msexcel =
	{
		ZLANG_OBJECT_msexcel , /* char *ancestor_name */
		
		ZlangCreateDirectProperty_msexcel , /* ZlangCreateDirectPropertyFunction *create_entity_func */
		ZlangDestroyDirectProperty_msexcel , /* ZlangDestroyDirectPropertyFunction *destroy_entity_func */
		
		NULL , /* ZlangFromCharPtrFunction *from_char_ptr_func */
		NULL , /* ZlangToStringFunction *to_string_func */
		NULL , /* ZlangFromDataPtrFunction *from_data_ptr_func */
		NULL , /* ZlangGetDataPtrFunction *get_data_ptr_func */
		
		NULL , /* ZlangOperatorFunction *oper_PLUS_func */
		NULL , /* ZlangOperatorFunction *oper_MINUS_func */
		NULL , /* ZlangOperatorFunction *oper_MUL_func */
		NULL , /* ZlangOperatorFunction *oper_DIV_func */
		NULL , /* ZlangOperatorFunction *oper_MOD_func */
		
		NULL , /* ZlangUnaryOperatorFunction *unaryoper_NEGATIVE_func */
		NULL , /* ZlangUnaryOperatorFunction *unaryoper_NOT_func */
		NULL , /* ZlangUnaryOperatorFunction *unaryoper_BIT_REVERSE_func */
		NULL , /* ZlangUnaryOperatorFunction *unaryoper_PLUS_PLUS_func */
		NULL , /* ZlangUnaryOperatorFunction *unaryoper_MINUS_MINUS_func */
		
		NULL , /* ZlangCompareFunction *comp_EGUAL_func */
		NULL , /* ZlangCompareFunction *comp_NOTEGUAL_func */
		NULL , /* ZlangCompareFunction *comp_LT_func */
		NULL , /* ZlangCompareFunction *comp_LE_func */
		NULL , /* ZlangCompareFunction *comp_GT_func */
		NULL , /* ZlangCompareFunction *comp_GE_func */
		
		NULL , /* ZlangLogicFunction *logic_AND_func */
		NULL , /* ZlangLogicFunction *logic_OR_func */
		
		NULL , /* ZlangLogicFunction *bit_AND_func */
		NULL , /* ZlangLogicFunction *bit_XOR_func */
		NULL , /* ZlangLogicFunction *bit_OR_func */
		NULL , /* ZlangLogicFunction *bit_MOVELEFT_func */
		NULL , /* ZlangLogicFunction *bit_MOVERIGHT_func */
		
		ZlangSummarizeDirectPropertySize_msexcel , /* ZlangSummarizeDirectPropertySizeFunction *summarize_direct_prop_size_func */
	} ;

ZlangImportObjectFunction ZlangImportObject_msexcel;
struct ZlangObject *ZlangImportObject_msexcel( struct ZlangRuntime *rt )
{
	struct ZlangObject	*obj = NULL ;
	struct ZlangFunction	*func = NULL ;
	int			nret = 0 ;
	
	nret = ImportObject( rt , & obj , ZLANG_OBJECT_msexcel , & direct_funcs_msexcel , sizeof(struct ZlangDirectFunctions) , NULL ) ;
	if( nret )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_LINK_FUNC_TO_ENTITY , "import object to global objects heap" )
		return NULL;
	}
	
	/* msexcel.LoadFile(string) */
	func = AddFunctionAndParametersInObject( rt , obj , "LoadFile" , "LoadFile(string)" , ZlangInvokeFunction_msexcel_LoadFile_string , ZLANG_OBJECT_bool , ZLANG_OBJECT_string,NULL , NULL ) ;
	if( func == NULL )
		return NULL;
	
	/* msexcel.SetConvertEncoding(string) */
	func = AddFunctionAndParametersInObject( rt , obj , "SetConvertEncoding" , "SetConvertEncoding(string)" , ZlangInvokeFunction_msexcel_SetConvertEncoding_string , ZLANG_OBJECT_bool , ZLANG_OBJECT_string,NULL , NULL ) ;
	if( func == NULL )
		return NULL;
	
	/* msexcel.GetSheetList() */
	func = AddFunctionAndParametersInObject( rt , obj , "GetSheetList" , "GetSheetList()" , ZlangInvokeFunction_msexcel_GetSheetList , ZLANG_OBJECT_list , NULL ) ;
	if( func == NULL )
		return NULL;
	
	/* msexcel.SelectSheet(string) */
	func = AddFunctionAndParametersInObject( rt , obj , "SelectSheet" , "SelectSheet(string)" , ZlangInvokeFunction_msexcel_SelectSheet_string , ZLANG_OBJECT_bool , ZLANG_OBJECT_string,NULL , NULL ) ;
	if( func == NULL )
		return NULL;
	
	/* msexcel.GetRowCount() */
	func = AddFunctionAndParametersInObject( rt , obj , "GetRowCount" , "GetRowCount()" , ZlangInvokeFunction_msexcel_GetRowCount , ZLANG_OBJECT_int , NULL ) ;
	if( func == NULL )
		return NULL;
	
	/* msexcel.GetColumnCount() */
	func = AddFunctionAndParametersInObject( rt , obj , "GetColumnCount" , "GetColumnCount()" , ZlangInvokeFunction_msexcel_GetColumnCount , ZLANG_OBJECT_int , NULL ) ;
	if( func == NULL )
		return NULL;
	
	/* msexcel.GetCellString(int,int) */
	func = AddFunctionAndParametersInObject( rt , obj , "GetCellString" , "GetCellString(int,int)" , ZlangInvokeFunction_msexcel_GetCellString_int_int , ZLANG_OBJECT_string , ZLANG_OBJECT_int,NULL , ZLANG_OBJECT_int,NULL , NULL ) ;
	if( func == NULL )
		return NULL;
	
	return obj ;
}

